JavaScript闭包
概述
一般可理解为函数(函数outerFunc())内的函数(innerFunc()),innerFunc函数可以访问其原型链(包括outerFunc())上的属性和方法。
JavaScript 没有块级作用域,只有函数作用域,所以闭包的使用与函数紧密相关。
1 | function Counter(start) { |
这里 Counter 返回两个闭包,即increment()和get(),这两个函数保持对外部函数Counter()的访问。
JS 不存在块级作用域,下例:
1 | for ( var i = 0; i < 10; i++ ) { |
此时先执行10次循环,同时i累积到10,然后约1000毫秒后,连续执行十次console.log(i),自然结果是十个10了。
为了避免上面的错误,可以用函数将变量作用域包围起来,方案一如下:
1 | for (var i = 0; i < 10; i++) { |
上面立即匿名执行函数将变量i传递到函数内,等待1秒后再将该变量打印出来,这也是在for循环内创建作用域的常见方式。
上面代码换一换,也能完成相同任务:
1 | for(var i = 0; i < 10; i++) { |
或是这样:
1 | for(var i = 0; i < 10; i++) { |
或是用bind():
1 | for(var i = 0; i < 10; i++) { |
总之,当要使用块级作用域时,我最推荐方案一,简单直白。
本文转载自JavaScript花园的闭包章节。
又看了个闭包的博客,源码如下:
1 | function createFunctions(){ |
我琢磨了下,改成如下:
1 | function createFunctions(){ |
结果是一样的,都是使用立即执行函数将每次for循环的结果保存在result数组中,只是原代码中返回的数组,数组项是函数,然后由函数返回数值。我写的更简单一点,数组项直接是数值了。如果第4行的赋值是:
1 | result[i] = function() { |
那么数组中保存的就是=后面的匿名函数,当第二个for循环执行时,因为createFunctions()中的i的值是10,所以结果就是十个10了。
另外一个例子:
1 | function increment() { |
第一次运行i()时,num的值是1,所以打印结果1。第二次运行时,其实只运行了function() {console.log(num++);}这一段代码,而不是整个increment(),所以第二次第三次运行时根本没有var num = 1这个过程。
PS:闭包不能在window的上下文中访问,要通过Function.prototype.bind来实现?